use bevy_app::{Plugin, PostUpdate};
use bevy_asset::Handle;
use bevy_color::{Alpha, Color};
use bevy_ecs::{
    bundle::Bundle,
    children,
    component::Component,
    hierarchy::Children,
    query::Changed,
    reflect::ReflectComponent,
    spawn::SpawnRelated,
    system::{Commands, Query},
};
use bevy_reflect::{prelude::ReflectDefault, Reflect};
use bevy_ui::{BackgroundColor, BorderRadius, Node, PositionType, Val};
use bevy_ui_render::ui_material::MaterialNode;

use crate::{
    alpha_pattern::{AlphaPattern, AlphaPatternMaterial},
    constants::size,
    palette,
};

/// Marker identifying a color swatch.
#[derive(Component, Default, Clone, Reflect)]
#[reflect(Component, Clone, Default)]
pub struct ColorSwatch;

/// Component that contains the value of the color swatch. This is copied to the child element
/// background.
#[derive(Component, Default, Clone, Reflect)]
#[reflect(Component, Clone, Default)]
pub struct ColorSwatchValue(pub Color);

/// Marker identifying the color swatch foreground, the piece that actually displays the color
/// in front of the alpha pattern. This exists so that users can reach in and change the color
/// dynamically.
#[derive(Component, Default, Clone, Reflect)]
#[reflect(Component, Clone, Default)]
pub struct ColorSwatchFg;

/// Template function to spawn a color swatch.
///
/// # Arguments
/// * `overrides` - a bundle of components that are merged in with the normal swatch components.
pub fn color_swatch<B: Bundle>(overrides: B) -> impl Bundle {
    (
        Node {
            height: size::ROW_HEIGHT,
            min_width: size::ROW_HEIGHT,
            border_radius: BorderRadius::all(Val::Px(5.0)),
            ..Default::default()
        },
        ColorSwatch,
        ColorSwatchValue::default(),
        AlphaPattern,
        MaterialNode::<AlphaPatternMaterial>(Handle::default()),
        overrides,
        children![(
            Node {
                position_type: PositionType::Absolute,
                left: Val::Px(0.),
                top: Val::Px(0.),
                bottom: Val::Px(0.),
                right: Val::Px(0.),
                border_radius: BorderRadius::all(Val::Px(5.0)),
                ..Default::default()
            },
            ColorSwatchFg,
            BackgroundColor(palette::ACCENT.with_alpha(0.5)),
        )],
    )
}

fn update_swatch_color(
    q_swatch: Query<(&ColorSwatchValue, &Children), Changed<ColorSwatchValue>>,
    mut commands: Commands,
) {
    for (value, children) in q_swatch.iter() {
        if let Some(first_child) = children.first() {
            commands
                .entity(*first_child)
                .insert(BackgroundColor(value.0));
        }
    }
}

/// Plugin which registers the observers for updating the swatch color.
pub struct ColorSwatchPlugin;

impl Plugin for ColorSwatchPlugin {
    fn build(&self, app: &mut bevy_app::App) {
        app.add_systems(PostUpdate, update_swatch_color);
    }
}
